package com.nuanshui.heatedloan.search.facade.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.nuanshui.heatedloan.search.base.annotation.Document;
import com.nuanshui.heatedloan.search.base.annotation.SearchField;
import com.nuanshui.heatedloan.search.base.annotation.SearchFieldFactor;
import com.nuanshui.heatedloan.search.base.annotation.SearchSortField;
import com.nuanshui.heatedloan.search.base.enums.SearchParamType;
import com.nuanshui.heatedloan.search.facade.ElasticSearchService;
import com.nuanshui.heatedloan.search.facade.model.ElasticSearchRequest;
import com.nuanshui.heatedloan.search.facade.model.ElasticSearchResponse;
import com.nuanshui.heatedloan.search.facade.model.FieldValueFactor;
import com.nuanshui.heatedloan.search.facade.model.SearchParams;

@Component
public class SearchUtil {
	
	//@Reference(retries=-1,timeout=50000)
	private ElasticSearchService elasticSearchService;
		
	public boolean remove(Object object) {
		 Class<?> clazz =  object.getClass();
    	 Document document = getDocument(clazz);
    	 if(document != null && elasticSearchService!=null){
    		String index = document.index();
    		String type = document.type();
    		String id;		
			try {		
				id = BeanUtils.getProperty(object, "id");
				return elasticSearchService.removeDocument(index, type, id);
			}catch(Exception e) {
				e.printStackTrace();
			}
			return false;
			
    	 }
    	 return false;
	}
	/**
	 * 
	 * 创建索引
	 * @param @param object
	 * @param @return 
	 * @author guohao
	 * @date 2016年4月5日 上午10:11:49
	 * @since 2.6.0
	 */
	public boolean createIndex(Object object) {
		 if(elasticSearchService == null) return false;
		 Class<?> clazz =  object.getClass();
    	 Document document = getDocument(clazz);
    	 if(document != null && elasticSearchService!=null){
    		String index = document.index();
    		String type = document.type();
    		String id;		
			try {		
				id = BeanUtils.getProperty(object, "id");
				elasticSearchService.createIndex(index, type, id, JSON.toJSONString(object));
				return true;
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch(Exception e){
				e.printStackTrace();
			}
    	 } 	 
    	 return false;
	}
	
	/**
	 * 获取搜索结果id
	 *
	 * @param @param t
	 * @param @param pager
	 * @param @return 
	 * @author guohao
	 * @date 2016年4月5日 上午10:12:07
	 * @since 2.6.0
	 */
	public <T extends Object> List<String> findIds(T t,Pageable pager) {
		 
		List<String> ids = new ArrayList<String>();
		if(elasticSearchService == null) return ids;
		  List<T> list =  find(t, pager);
            try {
				for(T temp :list){
				    ids.add(BeanUtils.getProperty(temp, "id"));
				}
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		  
		  return ids;
	}
	
	/**
	 * 搜索结果
	 * @param 
	 * @author guohao
	 * @date 2016年3月29日 下午6:36:36
	 */
	public <T extends Object> List<T> find(T t,Pageable pager) {
		 ElasticSearchRequest request = new ElasticSearchRequest();
		 request.setSize(pager.getPageSize());
		 request.setPage(pager.getPageNumber());
		 ArrayList<T> list = new ArrayList<T>();
		 if(elasticSearchService == null) return list;
		 @SuppressWarnings("unchecked")
		Class<T> clazz =  (Class<T>) t.getClass();
    	 Document document = getDocument(clazz);
    	 if(document != null && elasticSearchService!=null){
    		String index = document.index();
    		String type = document.type();
    		request.setIndex(index);
    		request.setType(type);
    		parseSearchParams(clazz, t, request);
    		ElasticSearchResponse response  = elasticSearchService.findDocument(request);
    		List<String> result = response.getData();
    		for(String str:result) {
    			list.add(JSON.parseObject(str,clazz));
    		}
    	 } 	 	 
    	 return list;
	}
	
	/**
	 * 
	 *获取个数
	 * 
	 * @author guohao
	 * @date 2016年4月5日 上午10:13:26
	 * @since $
	 */
	@SuppressWarnings("unchecked")
	public  <T extends Object> long count(T t) {
		if(elasticSearchService == null) return 0;
		 Class<T> clazz =  (Class<T>) t.getClass();
	   	 Document document = clazz.getAnnotation(Document.class);
	   	 if(document != null && elasticSearchService!=null){
	   		ElasticSearchRequest request = new ElasticSearchRequest();
	   		request.setSize(0);
	   		request.setPage(0);
	   		String index = document.index();
	   		String type = document.type();
	   		request.setIndex(index);
	   		request.setType(type);
	   		parseParams(clazz, t, request);
	   		ElasticSearchResponse response  = elasticSearchService.findDocument(request);
	   		return response.getTotal();
	   	 }
	   	 return 0;
	}
		
	private HashMap<String,String> parseParams(Class<?> clazz,Object object,ElasticSearchRequest request) {
	   HashMap<String,String> map = new HashMap<String, String>();
	  try {
		  List<Field> fields = getFields(clazz);
		   if(fields!=null&&fields.size()>0) {
			   for(Field field :fields) {
				   String key = field.getName();
				   
				   if(field.isAnnotationPresent(SearchField.class)) {
					    String value = BeanUtils.getProperty(object,key);
					    SearchField searchField = field.getAnnotation(SearchField.class);					
					    if(StringUtils.isNotEmpty(value)) {
					    	request.addParam(key, value);
					    }
				    
					    if(searchField.highLine()) {
					    	request.addHighLineFiled(key);
					    }
				  }		  
				  if(field.isAnnotationPresent(SearchSortField.class)) {
					  SearchSortField searchSortField = field.getAnnotation(SearchSortField.class);
					  String  sortField = searchSortField.field();
					  String value = BeanUtils.getProperty(object,key);
					  if(StringUtils.isNotEmpty(value)) {
						  if("1".equals(value)) {
							  request.addSortFiled(sortField, ElasticSearchRequest.ORDER_DIRECTION_ASC);

						  }else {
							  request.addSortFiled(sortField, ElasticSearchRequest.ORDER_DIRECTION_DESC);

						  }
					  }
					
				  }
			   }
		   }
	} catch (SecurityException e) {
		e.printStackTrace();
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	} catch (InvocationTargetException e) {
		e.printStackTrace();
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	}
	   
	   return map;
		
	}
	
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void parseSearchParams(Class<?> clazz,Object object,ElasticSearchRequest request) {
		try {
			  List<Field> fields = getFields(clazz);
			   if(fields!=null&&fields.size()>0) {
				   List<SearchParams> searchParams = Lists.newArrayList();
				   for(Field field :fields) {
					   String key = field.getName();
					   Class fieldType = field.getType();
					   if(field.isAnnotationPresent(SearchField.class)) {
						    String value = BeanUtils.getProperty(object,key);
						    SearchField searchField = field.getAnnotation(SearchField.class);					
						    if(StringUtils.isNotEmpty(value)) {
						    	SearchParams param = new SearchParams();
						    	param.setSearchType(searchField.searchType());
						    	param.setValue(value);
						    	if(searchField.searchType()== SearchParamType.MULTI_MATCH){
						    		String fieldNames[]  =  searchField.field();
						    		float boosts[] = searchField.boost();
						    		int boostLength = boosts.length;
						    		for(int i = 0;i<fieldNames.length;i++){
						    			if(i<boostLength) {
						    				param.addField(fieldNames[i],boosts[i]);
						    			}else {
						    				param.addField(fieldNames[i]);
						    			}
						    		}			    		
						    	}else if(fieldType.isAssignableFrom(HashMap.class)) {
						    								    		
						    		String fieldName = 	searchField.field()[0];
						    		if(StringUtils.isEmpty(fieldName)) {
						    			fieldName = key;
						    		}
						    		float boost = searchField.boost()[0];
						    		String getMethod = "get"+toUpperCase(fieldName, 0, 1);
						    		HashMap jsonObject = (HashMap) clazz.getMethod(getMethod).invoke(object);
						    		Set<String> keys =  jsonObject.keySet();				    		
						    		for(String k :keys){
						    			param = new SearchParams();
						    			param.setSearchType(searchField.searchType());
						    			param.setValue((String)jsonObject.get(k));
						    			param.addField(fieldName+"."+k,boost);		
						    			searchParams.add(param);
						    		}					    		
          					    	continue;					    		
							
						    	}else {
						    		String fieldName = 	searchField.field()[0];
						    		if(StringUtils.isEmpty(fieldName)) {
						    			fieldName = key;
						    		}
						    		float boost = searchField.boost()[0];
						    		
						    		param.addField(fieldName, boost);
						    	}
					
						    	searchParams.add(param);
						    		
						    }
					    
						    if(searchField.highLine()) {
						    	request.addHighLineFiled(key);
						    }
					  }
					   
					   request.setSearchParams(searchParams);
					  if(field.isAnnotationPresent(SearchSortField.class)) {
						  SearchSortField searchSortField = field.getAnnotation(SearchSortField.class);
						  String  sortField = searchSortField.field();
						  String value = BeanUtils.getProperty(object,key);
						  if(StringUtils.isNotEmpty(value)){
							  if("1".equals(value)) {
								  request.addSortFiled(sortField, ElasticSearchRequest.ORDER_DIRECTION_ASC);

							  }else {
								  request.addSortFiled(sortField, ElasticSearchRequest.ORDER_DIRECTION_DESC);

							  }
						  }
 						 
					  }
					  
					  
					  
					  //处理加分问题
					  if(field.isAnnotationPresent(SearchFieldFactor.class)) {
						  
						  SearchFieldFactor searchFieldFactor = field.getAnnotation(SearchFieldFactor.class);
						  FieldValueFactor fieldValueFactor = new FieldValueFactor();
						  if(StringUtils.isNotEmpty(searchFieldFactor.field())) {
							  fieldValueFactor.setField(searchFieldFactor.field());
						  }else {
							  fieldValueFactor.setField(key);
						  }
						  
						  fieldValueFactor.setMissing(searchFieldFactor.missing());
						  fieldValueFactor.setFactor(searchFieldFactor.factor());
						  fieldValueFactor.setWeight(searchFieldFactor.weight());
						  request.setFieldValueFactor(fieldValueFactor); 
					  }
				   }
			   }
			   
			   
			  
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
		
	}
	
	
	private  String toUpperCase(final String str, final int start,
			final int end) {

		if (StringUtils.isEmpty(str)) {
			return str;
		}
		StringBuilder sb = new StringBuilder();
		sb.append(str.substring(0, start));
		sb.append(str.substring(start, end).toUpperCase());
		sb.append(str.substring(end));
		return sb.toString();
	}
	private Document getDocument(Class<?> clazz) {
		for(;clazz!=Object.class;clazz=clazz.getSuperclass()) {
			Document document =  clazz.getAnnotation(Document.class);
			if(document!=null) return document;
			
		}
		return null;
		
	}
	
	private List<Field> getFields(Class<?> clazz) {
		List<Field> fields = new ArrayList<Field>();
		for(;clazz!=Object.class;clazz=clazz.getSuperclass()) {
			Field tmps[] = clazz.getDeclaredFields();
			for(Field field :tmps) {
				fields.add(field);
			}
		}
		return fields;
		
	}
	
}
